package org.lex.ftplist.core.old;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.net.ftp.FTPFile;
import org.lex.collection.tree.ITreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FTPTreeComparator_OLD {
	private Logger log = LoggerFactory.getLogger(FTPTreeComparator_OLD.class);
	private Collection<Pair<ITreeNode>> sameOrUpdate = new HashSet<Pair<ITreeNode>>();

	public CompareResult<ITreeNode> compareTree(ITreeNode oldTree, ITreeNode newTree) {
		CompareResult<ITreeNode> root = this.compareNodes(list(oldTree.getChildren()), list(newTree.getChildren()));

		while (true) {
			Pair<ITreeNode> top = null;
			for (Iterator<Pair<ITreeNode>> iter = sameOrUpdate.iterator(); iter.hasNext();) {
				Pair<ITreeNode> p = iter.next();
				if (false == p.visited) {
					top = p;
					break;
				}
			}
			if (null == top) {
				break;
			}
			top.visited = true;
			CompareResult<ITreeNode> r = this.compareTree(top.oldObject, top.newObject);
			root.added.addAll(r.added);
			root.deleted.addAll(r.deleted);
		}
		return root;
	}

	public CompareResult<ITreeNode> compareNode(ITreeNode oldNodes, ITreeNode newNodes) {
		return compareNodes(list(oldNodes), list(newNodes));
	}

	public CompareResult<ITreeNode> compareNodes(List<ITreeNode> oldNodes, List<ITreeNode> newNodes) {
		CompareResult<ITreeNode> results = new CompareResult<ITreeNode>();
		for (ITreeNode oldNode : oldNodes) {
			ITreeNode newNode = this.findNode(oldNode, newNodes);
			log.info("old=[" + oldNode + "], new=[" + newNode + "]");
			if (null == newNode) {
				results.deleted.add(oldNode);
			} else {
				this.addSameOrUpdate(oldNode, newNode);
				newNodes.remove(newNode);
			}
		}
		results.added.addAll(newNodes);
		return results;
	}

	/**
	 * find an ITreeNode from <code>source</code>, who has the same type, and
	 * same name wigh <code>target</code>
	 * 
	 * @param target
	 * @param source
	 */
	private ITreeNode findNode(ITreeNode target, Collection<ITreeNode> source) {
		FTPFile targetFile = (FTPFile) target.getData();
		boolean isFile = targetFile.isFile();
		boolean isDir = targetFile.isDirectory();
		String name = targetFile.getName();

		for (ITreeNode src : source) {
			FTPFile file = (FTPFile) src.getData();
			if (file.isFile() == isFile && file.isDirectory() == isDir && file.getName().equals(name)) {
				return src;
			}
		}
		return null;
	}

	private static <T> List<T> list(T... data) {
		List<T> list = new ArrayList<T>();
		Collections.addAll(list, data);
		return list;
	}

	private void addSameOrUpdate(ITreeNode oldNode, ITreeNode newNode) {
		Pair<ITreeNode> p = Pair.valueOf(oldNode, newNode);
		if (false == sameOrUpdate.contains(p)) {
			this.sameOrUpdate.add(p);
		}
	}

	public static class CompareResult<T> {
		public List<T> added = new ArrayList<T>();
		public List<T> deleted = new ArrayList<T>();

		public String toString() {
			StringBuilder buf = new StringBuilder();
			buf.append("ADD: ").append(added).append('\n');
			buf.append("DEL: ").append(deleted).append('\n');
			return buf.toString();
		}
	}

	private static class Pair<T> {
		public T oldObject;
		public T newObject;
		public boolean visited = false;

		public static <T> Pair<T> valueOf(T o, T n) {
			Pair<T> p = new Pair<T>();
			p.oldObject = o;
			p.newObject = n;
			return p;
		}

		public String toString() {
			return "[old=" + oldObject + ", new=" + newObject + "]\n";
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((newObject == null) ? 0 : newObject.hashCode());
			result = prime * result + ((oldObject == null) ? 0 : oldObject.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Pair other = (Pair) obj;
			if (newObject == null) {
				if (other.newObject != null)
					return false;
			} else if (!newObject.equals(other.newObject))
				return false;
			if (oldObject == null) {
				if (other.oldObject != null)
					return false;
			} else if (!oldObject.equals(other.oldObject))
				return false;
			return true;
		}
	}
}
